﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using ArgusLib.Drawing;

namespace ArgusLib.Controls
{
	[RequiresAssembly(typeof(System.Drawing.ArgusLibExtensions))]
	public class RectangleSelectionLayer : Control
	{
		//PointF[] rectangleToDraw;
		GraphicsPath graphicsPath;
		Point refPoint;
		RectangleA refSelection;
		Status status;
		Keys keyModeRotating = Keys.ControlKey;
		Keys keyModeMoving = Keys.ShiftKey;
		float crossSize = 10;

		public Pen BorderPen { get; set; }
		public Brush RectangleBrush { get; set; }
		public SelectionMode SelectionMode { get; set; }
		public float RectangleRatio { get; set; }

		RectangleA selection;
		public RectangleA Selection
		{
			get { return this.selection; }
			private set
			{
				this.selection = value;
				this.OnSelectionChanged(EventArgs.Empty);
			}
		}

		public event EventHandler SelectionChanged;
		public event EventHandler SelectionChangingBegin;
		public event EventHandler SelectionChangingEnd;
		public event EventHandler RectangleSelected;

		public RectangleSelectionLayer()
		{
			this.status = Status.None;
			this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
			this.SetStyle(ControlStyles.UserPaint, true);
			this.BackColor = Color.Transparent;
			this.DoubleBuffered = true;
			this.BorderPen = new Pen(Color.FromArgb(128, Color.White));
			this.RectangleBrush = new SolidBrush(Color.FromArgb(64, Color.Blue));
			this.SelectionMode = ArgusLib.Controls.SelectionMode.Default;
		}

		void OnSelectionChanged(EventArgs e)
		{
			if (this.SelectionChanged != null)
				this.SelectionChanged(this, e);
		}

		void OnSelectionChangingBegin(EventArgs e)
		{
			if (this.SelectionChangingBegin != null)
				this.SelectionChangingBegin(this, e);
		}

		void OnSelectionChangingEnd(EventArgs e)
		{
			if (this.SelectionChangingEnd != null)
				this.SelectionChangingEnd(this, e);
		}

		void OnRectangleSelected(EventArgs e)
		{
			if (this.RectangleSelected != null)
				this.RectangleSelected(this, e);
		}

		protected override void OnMouseDown(MouseEventArgs e)
		{
			this.Focus();
			if (e.Button == System.Windows.Forms.MouseButtons.Left)
			{
				if (this.status == Status.None)
				{
					this.OnSelectionChangingBegin(EventArgs.Empty);
					this.status = Status.Resizing;
					this.Selection = new RectangleA(e.Location, SizeF.Empty, 0);
				}
				else
				{
					this.status = Status.None;

					this.OnSelectionChangingEnd(EventArgs.Empty);
					this.OnRectangleSelected(EventArgs.Empty);
				}
			}
			else if (e.Button == System.Windows.Forms.MouseButtons.Right)
			{
				this.status = Status.None;
				this.OnSelectionChangingEnd(EventArgs.Empty);
			}
			this.SetGraphicsFromSelection();
			base.OnMouseDown(e);
		}

		protected override void OnKeyDown(KeyEventArgs e)
		{
			if (this.status == Status.Resizing)
			{
				if (this.SelectionMode.HasFlag(SelectionMode.AllowRotation) == true && e.KeyCode == this.keyModeRotating)
				{
					this.status = Status.Rotating;
					this.refPoint = this.PointToClient(Cursor.Position);
					this.refSelection = this.Selection;
					this.SetGraphicsFromSelection();
				}
				else if (this.SelectionMode.HasFlag(SelectionMode.AllowMoving) == true && e.KeyCode == this.keyModeMoving)
				{
					this.status = Status.Moving;
					this.refPoint = this.PointToClient(Cursor.Position);
					this.refSelection = this.Selection;
					this.SetGraphicsFromSelection();
				}
			}
			base.OnKeyDown(e);
		}

		protected override void OnKeyUp(KeyEventArgs e)
		{
			if (this.status == Status.Rotating && e.KeyCode == this.keyModeRotating)
			{
				this.status = Status.Resizing;
				this.SetGraphicsFromSelection();
			}
			else if (this.status == Status.Moving && e.KeyCode == this.keyModeMoving)
			{
				this.status = Status.Resizing;
				this.SetGraphicsFromSelection();
			}

			base.OnKeyUp(e);
		}

		protected override void OnMouseMove(MouseEventArgs e)
		{
			if (this.status == Status.Resizing)
			{
				float dx = e.X - this.Selection.Center.X;
				float dy = e.Y - this.Selection.Center.Y;
				double d = 2*Math.Sqrt(dx * dx + dy * dy);
				double beta = Math.Atan2(dy, dx);
				double phi = beta - this.Selection.Rotation;

				RectangleA sel = this.Selection;
				sel.Size = new SizeF(
					(float)Math.Abs(d * Math.Cos(phi)),
					(float)Math.Abs(d * Math.Sin(phi)));

				if (this.RectangleRatio > 0)
					sel.Size = sel.Size.ChangeRatio(this.RectangleRatio, Drawing.ChangeRatioOptions.IncreaseAreaSize);

				this.Selection = sel;
			}
			else if (this.status == Status.Moving)
			{
				RectangleA sel = this.refSelection;
				sel.X += e.X - this.refPoint.X;
				sel.Y += e.Y - this.refPoint.Y;
				this.Selection = sel;
			}
			else if (this.status == Status.Rotating)
			{
				double refAngle = Math.Atan2(this.refPoint.Y - this.refSelection.Y, this.refPoint.X - this.refSelection.X);
				double angle = Math.Atan2(e.Y - this.refSelection.Y, e.X - this.refSelection.X);
				RectangleA sel = this.refSelection;
				sel.Rotation += (float)(angle - refAngle);
				this.Selection = sel;
			}
			else
			{
				base.OnMouseMove(e);
				return;
			}

			this.SetGraphicsFromSelection();
			base.OnMouseMove(e);
		}

		void SetGraphicsFromSelection()
		{
			if (this.graphicsPath == null)
				this.graphicsPath = new GraphicsPath();
			else
				this.graphicsPath.Reset();

			if (this.status != Status.None)
			{
				PointF[] rect = new PointF[4];
				rect[0] = this.Selection.BottomLeft;
				rect[1] = this.Selection.TopLeft;
				rect[2] = this.Selection.TopRight;
				rect[3] = this.Selection.BottomRight;
				this.graphicsPath.AddLines(rect);
				this.graphicsPath.CloseFigure();

				if (this.status == Status.Rotating)
				{
					PointF start = new PointF(
						(rect[2].X + rect[3].X) / 2,
						(rect[2].Y + rect[3].Y) / 2);
					this.graphicsPath.AddLine(start, this.Selection.Center);
					this.graphicsPath.CloseFigure();
				}
				else if (this.status == Status.Moving)
				{
					this.graphicsPath.AddLine(this.Selection.X - this.crossSize / 2, this.Selection.Y, this.Selection.X + this.crossSize / 2, this.Selection.Y);
					this.graphicsPath.CloseFigure();
					this.graphicsPath.AddLine(this.Selection.X, this.Selection.Y - this.crossSize / 2, this.Selection.X, this.Selection.Y + this.crossSize / 2);
					this.graphicsPath.CloseFigure();
				}
			}
			this.Invalidate();
		}

		protected override void OnPaint(PaintEventArgs e)
		{
			base.OnPaint(e);
			if (this.graphicsPath != null)
			{
				if (this.RectangleBrush != null)
					e.Graphics.FillPath(this.RectangleBrush, this.graphicsPath);
				if (this.BorderPen != null)
					e.Graphics.DrawPath(this.BorderPen, this.graphicsPath);
			}
		}

		enum Status : byte
		{
			None,
			Resizing,
			Rotating,
			Moving
		}
	}

	[Flags]
	public enum SelectionMode : byte
	{
		//FixedEdge = 1,
		//FixedCenter = 1 << 1,
		AllowRotation = 1 << 2,
		AllowMoving = 1 << 3,

		Default = 0,
		FullControl = AllowRotation | AllowMoving
	}
}
